package com.klamerek.rainbow.client.charts;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.google.gwt.widgetideas.graphics.client.Color;
import com.google.gwt.widgetideas.graphics.client.GWTCanvas;
import com.klamerek.rainbow.client.charts.geom.MyCircle;
import com.klamerek.rainbow.client.charts.geom.MyIntersection;
import com.klamerek.rainbow.client.charts.geom.MyLine;
import com.klamerek.rainbow.client.charts.geom.MyPoint;
import com.klamerek.rainbow.client.charts.geom.MySegment;

public class NGoneChart extends GWTCanvas {

	/**
	 * List of NGoneChartElt.
	 */
	private List<NGoneChartElt> listElt;

	/**
	 * The length of the graduation.
	 */
	private double graduationLength;

	/**
	 * Center of the chart.
	 */
	private MyPoint center;

	/**
	 * Map of extern points.
	 */
	private HashMap<NGoneChartElt, MyPoint> mapExternPoint;

	/**
	 * Map of the graduations.
	 */
	private HashMap<NGoneChartElt, MySegment[]> mapGraduation;
	
	/**
	 * Map of the values.
	 */
	private HashMap<NGoneChartElt, MyPoint> mapValuePoint;

	/**
	 * Constructor
	 * @param listElt
	 */
	public NGoneChart(List<NGoneChartElt> listElt){
		super(300,300);
		this.listElt = listElt;
		this.graduationLength = 4.0;
		this.initChart();
		this.drawChart();
	}

	/**
	 * Constructor
	 * @param listElt
	 * @param size
	 */
	public NGoneChart(List<NGoneChartElt> listElt,int size){
		super(size,size);
		this.listElt = listElt;
		this.graduationLength = 4.0;
		this.initChart();
		this.drawChart();
	}

	/**
	 * Constructor
	 * @param listElt
	 * @param size
	 * @param graduationLength
	 */
	public NGoneChart(List<NGoneChartElt> listElt,int size, double graduationLength){
		super(size,size);
		this.listElt = listElt;
		this.graduationLength = graduationLength;
		this.initChart();
		this.drawChart();
	}

	public void initChart(){
		this.mapExternPoint = new HashMap<NGoneChartElt, MyPoint>();
		this.mapGraduation = new HashMap<NGoneChartElt, MySegment[]>();
		this.mapValuePoint = new HashMap<NGoneChartElt, MyPoint>();
		
		this.center = new MyPoint(this.getCoordWidth()/2,this.getCoordHeight()/2);
		MyCircle externCircle = new MyCircle(this.center,(this.getCoordWidth()/2) - 10);

		int nbElt = 0;
		double angle = 0.0;
		if (this.listElt != null ){
			nbElt = this.listElt.size();
		}

		if (nbElt != 0){
			angle = 360 / nbElt;
		}

		double angleRadianOther = Math.PI * ((180 - angle) / 2) / 180 ;
		double lengthNGone = Math.cos(angleRadianOther) * 2*externCircle.getRadius(); 

		if (lengthNGone > 0){
			MyPoint firstPoint = new MyPoint(this.getCoordWidth()/2,10);
			List<MyPoint> listPoint = new ArrayList<MyPoint>();
			listPoint.add(firstPoint);

			//Calculate the points of the NGone
			MyPoint currentPoint = firstPoint;
			for(int i=0;i<nbElt;i++){
				MyCircle tempCircle = new MyCircle(currentPoint,lengthNGone);
				MyPoint point1 = MyIntersection.getIntersection2(externCircle, tempCircle);
				if (! MyPoint.ListContains(listPoint, point1, 0.01)){
					listPoint.add(point1);
					currentPoint = point1;
				}else{
					MyPoint point2 = MyIntersection.getIntersection1(externCircle, tempCircle);
					if (! MyPoint.ListContains(listPoint, point2, 0.01)){
						listPoint.add(point2);
						currentPoint = point2;
					}
				}
			}

			MyPoint[] points = listPoint.toArray(new MyPoint[] {});
			for(int i=0;i < points.length;i++){
				this.mapExternPoint.put(this.listElt.get(i), points[i]);
			}


			//Calculate graduations
			for(int i=0;i < points.length;i++){
				MySegment segment = new MySegment(this.center,points[i]);
				MyLine line = new MyLine(this.center,points[i]);
				NGoneChartElt elt = this.listElt.get(i);
				List<MySegment> listSegment = new ArrayList<MySegment>();
				double unitByValue = segment.getLength() / elt.getMaxValue();
				for (int j=0;j<elt.getMaxValue();j++){
					//We find the point on the segment (intern axe)
					MyCircle circleForSegment = new MyCircle(this.center,unitByValue * (j+1));
					MyPoint pointForInternAxe1 = MyIntersection.getIntersection1(line,circleForSegment);
					MyPoint pointForInternAxe2 = MyIntersection.getIntersection2(line,circleForSegment);
					MyPoint pointCenterForElt = null;
					if (segment.contains(pointForInternAxe1)){
						pointCenterForElt = pointForInternAxe1;
					}else if (segment.contains(pointForInternAxe2)){
						pointCenterForElt = pointForInternAxe2;
					}
					//If the current point corresponding to the value we store it
					if (elt.getValue() == (j+1)){
						this.mapValuePoint.put(elt, pointCenterForElt);
					}
					//Now we calculate the segment
					if (pointCenterForElt != null){
						MyCircle circleForGraduation = new MyCircle(pointCenterForElt,this.graduationLength);
						MyLine lineForGraduation = line.getPerpendicular(pointCenterForElt);
						MyPoint pointForGraduation1 = MyIntersection.getIntersection1(lineForGraduation,circleForGraduation);
						MyPoint pointForGraduation2 = MyIntersection.getIntersection2(lineForGraduation,circleForGraduation);
						listSegment.add(new MySegment(pointForGraduation1,pointForGraduation2));
					}
				}
				this.mapGraduation.put(this.listElt.get(i), listSegment.toArray(new MySegment[]{}));
			}
		}
	}

	/**
	 * Draw the chart.
	 */
	public void drawChart(){
		this.drawGone();
		this.drawGraduation();
	}

	private void drawGone(){

		List<MyPoint> listTemp = new ArrayList<MyPoint>();
		for(NGoneChartElt elt : this.listElt){
			listTemp.add(this.mapExternPoint.get(elt));
		}

		MyPoint[] points = listTemp.toArray(new MyPoint[]{});

		this.setStrokeStyle(Color.BLUE);
		this.beginPath();
		//Draw the intern axes
		for(int i=0;i < points.length - 1;i++){
			this.moveTo(this.center.getX(),this.center.getY());
			this.lineTo(points[i].getX(),points[i].getY());
		}
		this.moveTo(this.center.getX(),this.center.getY());
		this.lineTo(points[points.length - 1].getX(),points[points.length - 1].getY());

		//Draw the edges
		for(int i=0;i < points.length;i++){
			if (i == 0){
				this.moveTo(points[i].getX(),points[i].getY());
			}else{
				this.lineTo(points[i].getX(),points[i].getY());
			}
		}
		this.closePath();
		this.stroke();
	}

	private void drawGraduation(){

		//Draw graduations
		this.setStrokeStyle(Color.BLUE);
		this.beginPath();
		for(NGoneChartElt elt : this.listElt){
			MySegment[] tabSegment = this.mapGraduation.get(elt);
			for(int i=0;i<tabSegment.length;i++){
				MyPoint point1 = tabSegment[i].getAPoint();
				MyPoint point2 = tabSegment[i].getBPoint();
				this.moveTo(point1.getX(),point1.getY());
				this.lineTo(point2.getX(),point2.getY());
			}
		}
		this.closePath();
		this.stroke();

		//Draw values
		List<MyPoint> listValues = new ArrayList<MyPoint>();
		this.setStrokeStyle(Color.RED);
		this.beginPath();
		for(NGoneChartElt elt : this.listElt){
			MyPoint point = this.mapValuePoint.get(elt);
			this.arc(point.getX(), 
					point.getY(),
					this.graduationLength, 0, 0,true);
			listValues.add(point);
		}
		this.closePath();
		this.stroke();

		//Draw intern gone
		this.setStrokeStyle(Color.BLACK);
		this.beginPath();
		MyPoint[] tabPoints = listValues.toArray(new MyPoint[]{});
		for(int i=0;i<tabPoints.length - 1;i++){
			this.moveTo(tabPoints[i].getX(),tabPoints[i].getY());
			this.lineTo(tabPoints[i+1].getX(),tabPoints[i+1].getY());
		}
		this.moveTo(tabPoints[tabPoints.length - 1].getX(),tabPoints[tabPoints.length - 1].getY());
		this.lineTo(tabPoints[0].getX(),tabPoints[0].getY());
		
		this.closePath();
		this.stroke();
	}
}
